package com.shuke.medical.insure.component;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;


/***
 * @title BseHttpClientPoolUtil
 * @description: http 连接池
 * @author woo hua
 * @version 1.0.0
 * @create 2023/3/24 15:32
 **/
@Slf4j
@Component
public class BaseHttpClientPoolUtil {
    /*最大连接数，应该放在配置文件内*/
    private int maxTotal = 800;
    /*每个路由的最大连接数*/
    private int maxPerRoute = 200;
    /**
     * 向服务端请求超时时间设置(单位:毫秒)
     */
    private static int SERVER_REQUEST_TIME_OUT = 5000;
    /**
     * 服务端响应超时时间设置(单位:毫秒)
     */
    private static int SERVER_RESPONSE_TIME_OUT = 5000;

    /*从连接池获取连接的超时时间*/
    private static int CONNECTION_REQUEST_TIME_OUT = 5000;
    /*重试次数*/
    private static int retry = 3;
    //    /*是否关闭socket缓冲*/
//    private boolean tcpNoDelay = false;
//    /*关闭socket后，是否可立即重用端口*/
//    private boolean soReuseAddress = true;
//    /*接收数据等待超时时间毫秒*/
//    private int soTimeout = 500;
//    /*socket最大等待关闭连接时间秒*/
//    private int soLinger = 60;
    private static volatile PoolingHttpClientConnectionManager pollManager = null;

    public BaseHttpClientPoolUtil() throws NoSuchAlgorithmException {
        log.info("初始化http连接池...");
        init();
    }

    @DependsOn("httpClientPoolProperties")
    private void init() throws NoSuchAlgorithmException {
        if (pollManager == null) {
            synchronized (BaseHttpClientPoolUtil.class) {
                if (pollManager == null) {
                    LayeredConnectionSocketFactory sslsf = null;
                    sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault());
                    Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                            .register("https", sslsf)
                            .register("http", new PlainConnectionSocketFactory())
                            .build();
                    pollManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
                    pollManager.setMaxTotal(maxTotal);
                    pollManager.setDefaultMaxPerRoute(maxPerRoute);

                    //某个host的路由数配置，覆盖default,暂时不配置
                    //pollManager.setMaxPerRoute(new HttpRoute(new HttpHost("some host", 80)), 150);

//        //socket配置
//        SocketConfig socketConfig = SocketConfig.custom()
//                .setTcpNoDelay(tcpNoDelay)     //是否立即发送数据，设置为true会关闭Socket缓冲，默认为false
//                .setSoReuseAddress(soReuseAddress) //是否可以在一个进程关闭Socket后，即使它还没有释放端口，其它进程还可以立即重用端口
//                .setSoTimeout(soTimeout)       //接收数据的等待超时时间，单位ms
//                .setSoLinger(soLinger)         //关闭Socket时，要么发送完所有数据，要么等待60s后，就关闭连接，此时socket.close()是阻塞的
//                .setSoKeepAlive(true)    //开启监视TCP连接是否有效
//                .build();
//        pollManager.setDefaultSocketConfig(socketConfig);
                }

            }
        }
    }

    public CloseableHttpClient getHttpClient(int SERVER_REQUEST_TIME_OUT, int SERVER_RESPONSE_TIME_OUT, int CONNECTION_REQUEST_TIME_OUT) {
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(SERVER_REQUEST_TIME_OUT).setConnectTimeout(SERVER_RESPONSE_TIME_OUT).build();
        CloseableHttpClient httpClient = HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(pollManager)
                .setRetryHandler(getHttpRequestRetryHandler())
                .build();
        return httpClient;
    }

    public RequestConfig getDefaultRequestConfig() {
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(SERVER_REQUEST_TIME_OUT)
                .setConnectTimeout(SERVER_RESPONSE_TIME_OUT)
                .setConnectionRequestTimeout(CONNECTION_REQUEST_TIME_OUT)
                .build();
        return requestConfig;
    }


    public HttpRequestRetryHandler getHttpRequestRetryHandler() {
        HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {

            @Override
            public boolean retryRequest(
                    IOException exception,
                    int executionCount,
                    HttpContext context) {
                if (executionCount >= retry) {
                    // 如果已经重试了5次，就放弃
                    return false;
                }
                if (exception instanceof InterruptedIOException) {
                    // 超时
                    return false;
                }
                if (exception instanceof UnknownHostException) {
                    // 目标服务器不可达
                    return false;
                }
                if (exception instanceof ConnectTimeoutException) {
                    // 连接被拒绝
                    return false;
                }
                if (exception instanceof SSLException) {
                    // ssl握手异常
                    return false;
                }
                HttpClientContext clientContext = HttpClientContext.adapt(context);
                HttpRequest request = clientContext.getRequest();
                boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
                if (idempotent) {
                    // 如果请求是幂等的，就再次尝试
                    return true;
                }
                return false;
            }

        };
        return myRetryHandler;
    }

    public CloseableHttpClient getHttpClient() {
        RequestConfig requestConfig = getDefaultRequestConfig();
        CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig)
                .setConnectionManager(pollManager)
                .setRetryHandler(getHttpRequestRetryHandler())
                .build();
        return httpClient;
    }

    public String doGet(String url) throws Exception {
        if (StringUtils.isEmpty(url)) {
            throw new Exception("param can not be null");
        }

        //    url = validateUrl(url);

        CloseableHttpResponse response = null;
        String body = null;
        try {
            HttpGet get = new HttpGet(url);

            get.addHeader("Content-type", "application/json; charset=utf-8");
            get.setHeader("Accept", "application/json");

            response = getHttpClient().execute(get);

            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                body = EntityUtils.toString(response.getEntity());
            } else {
                throw new Exception(String.format("error code [%s]", statusCode));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != response) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return body;
    }

    /**
     * 执行post方法
     *
     * @param url
     * @param parameter
     * @return
     */
    public String doPost(String url, String parameter) throws Exception {
        if (StrUtil.isEmpty(url)) {
            throw new Exception("url can not be null");
        }

        //    url = validateUrl(url);

        String body = null;
        CloseableHttpResponse response = null;
        try {

            HttpPost httpPost = new HttpPost(url);

            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            httpPost.setHeader("Accept", "application/json");
            if (!StringUtils.isEmpty(parameter)) {
                httpPost.setEntity(new StringEntity(parameter, Charset.forName("UTF-8")));
            }

            response = getHttpClient().execute(httpPost);

            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                body = EntityUtils.toString(response.getEntity());
            } else {
                throw new Exception(String.format("error code []", statusCode));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != response) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return body;
    }

    public String doPut(String url, String parameter) throws Exception {
//    if(DataSongStringUtils.isEmpty(parameter) || DataSongStringUtils.isEmpty(url)){
//       throw new DataSongException(Status.PARAM_ERROR, "param can not be null");
//    }

        //    url = validateUrl(url);

        String body = null;
        CloseableHttpResponse response = null;
        try {
            HttpPut httpPost = new HttpPut(url);

            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            httpPost.setHeader("Accept", "application/json");
            if (null != parameter) {
                httpPost.setEntity(new StringEntity(parameter, Charset.forName("UTF-8")));
            }
            response = getHttpClient().execute(httpPost);

            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                body = EntityUtils.toString(response.getEntity());
            } else {
                throw new Exception(String.format("%s ,error code [%s]", response.getStatusLine().getReasonPhrase(), statusCode));
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != response) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return body;
    }

    public String doDelete(String url) throws Exception {
        if (StringUtils.isEmpty(url)) {
            throw new Exception("param can not be null");
        }

        //    url = validateUrl(url);

        String body = null;
        CloseableHttpResponse response = null;
        try {
            HttpDelete delete = new HttpDelete(url);

            delete.addHeader("Content-type", "application/json; charset=utf-8");
            delete.setHeader("Accept", "application/json");

            response = getHttpClient().execute(delete);

            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                // Read the response body
                body = EntityUtils.toString(response.getEntity());
            } else {
                throw new Exception(String.format("error code []", statusCode));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != response) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return body;
    }

    protected String validateUrl(String url) {
        if (url.contains("?")) {
            url = url.replace("?", "%26");
        }

        if (url.contains("&")) {
            url = url.replace("&", "%3F");
        }

        if (url.contains("|")) {
            url = url.replace("|", "%124");
        }

        if (url.contains("=")) {
            url = url.replace("=", "%3D");
        }

        if (url.contains("#")) {
            url = url.replace("#", "%23");
        }

        if (url.contains("/")) {
            url = url.replace("/", "%2F");
        }

        if (url.contains("+")) {
            url = url.replace("+", "%2B");
        }

        if (url.contains("%")) {
            url = url.replace("%", "%25");
        }

        if (url.contains(" ")) {
            url = url.replace(" ", "%20");
        }

        return url;
    }
}

